from argparse import ArgumentParser
from venv import EnvBuilder

import os.path as osp
import sys, logging, subprocess


class VirtualEnvCreator(EnvBuilder):
    """Virtual environment creator for each project. This creator
    scans the `requirements.txt` shared among all projects together
    with the dependencies for each project, and create a python
    virtual environment in the given folder.
    """

    def __init__(self, project_name: str, venv: str, install_deps: bool = False):
        """Initialize a virtual environment creator for a project.

        `project_name`: the name or folder of the project.

        `venv`: the target folder in which to create this virtual environment.

        `install_deps`: install packages right after the creation of venv.
        """

        self._check_configurations(project_name, venv, install_deps)
        super(VirtualEnvCreator, self).__init__(
            prompt=self.project_name, clear=True, with_pip=True, upgrade_deps=True
        )

    def post_setup(self, context):
        logging.info(f"To activate the venv, source one of the following scripts:")
        for activate_script in ["activate", "activate.bat", "Activate.ps1"]:
            logging.info(f'** "{osp.join(context.bin_path, activate_script)}"')

        requirements = self._search_requirements()

        if not self.install_deps and requirements:
            logging.warning(f'Find requirements for project "{self.project_name}":')
            for requirements_file in requirements:
                logging.warning(f'** "{requirements_file}"')
            logging.warning(
                f"Requirements are not installed, consider manual installation."
            )

        if self.install_deps and requirements:
            logging.info(f'Installing dependencies in the venv "{self.project_name}":')

            python_exe = self._get_python(context)
            for requirements_file in requirements:
                logging.info(f'Installing requirements in "{requirements_file}".')
                try:
                    cmd = [python_exe, "-m", "pip", "install", "-r", requirements_file]
                    subprocess.check_call(cmd)
                except subprocess.CalledProcessError as err:
                    logging.warning(
                        f'Cannot install all requirements in "{requirements_file}".'
                    )

    def create_venv(self):
        self.create(self.venv)

    def _check_configurations(self, project_name, venv, install_deps):
        repository = osp.abspath(osp.join(osp.dirname(__file__), ".."))
        project_path = osp.join(repository, project_name)

        if not osp.exists(project_path) or not osp.isdir(project_path):
            raise RuntimeError(
                f'Cannot find project "{project_name}" in "{repository}".'
            )

        self.repository = repository
        self.project_name = project_name
        self.project_path = project_path
        self.venv = osp.abspath(venv)
        self.install_deps = install_deps

        logging.info(f'Find project "{self.project_name}" at "{self.project_path}".')
        logging.info(f'Python virtual environment will be created in "{self.venv}".')

    def _get_python(self, context):
        python_exe = "python.exe" if sys.platform == "win32" else "python"
        return osp.join(context.bin_path, python_exe)

    def _search_requirements(self):
        requirements = []

        common_requirements = osp.join(self.repository, "requirements.txt")
        if osp.isfile(common_requirements):
            requirements.append(common_requirements)

        unique_requirements = osp.join(self.project_path, "requirements.txt")
        if osp.isfile(unique_requirements):
            requirements.append(unique_requirements)

        return requirements


def main_procedure(cmdargs):
    logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")

    VirtualEnvCreator(
        project_name=cmdargs.project,
        venv=cmdargs.venv,
        install_deps=cmdargs.install_deps,
    ).create_venv()


if __name__ == "__main__":
    parser = ArgumentParser(description="")

    parser.add_argument(
        "project",
        metavar="PROJECT_FOLDER",
        help="The folder of the target project using this virtual environment.",
    )
    parser.add_argument(
        "venv",
        metavar="VENV_FOLDER",
        help="The folder in which to create a python venv.",
    )
    parser.add_argument(
        "--install_deps",
        default=False,
        action="store_true",
        help="Whether dependencies should be automatically installed.",
    )

    main_procedure(parser.parse_args())
